package com.liujit.auth.wx.service;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.liujit.auth.domain.param.WxLoginParams;
import com.liujit.auth.domain.result.OauthToken;
import com.liujit.auth.domain.result.WxLoginResult;
import com.liujit.auth.security.jwt.JwtGenerator;
import com.liujit.bms.app.api.BmsAppFeignClient;
import com.liujit.bms.app.domain.model.BlogMember;
import com.liujit.bms.app.domain.params.BlogMemberParams;
import com.liujit.common.constants.AuthConstant;
import com.liujit.common.constants.DictCodeConstant;
import com.liujit.common.constants.GlobalConstant;
import com.liujit.common.constants.RedisKeyConstant;
import com.liujit.common.domain.result.DictItem;
import com.liujit.common.domain.result.MemberInfo;
import com.liujit.common.domain.result.Result;
import com.liujit.common.enums.ResultCode;
import com.liujit.common.exception.BusinessException;
import com.liujit.common.utils.DozerConvertor;
import com.liujit.redis.service.RedisService;
import com.liujit.upms.api.UpmsFeignClient;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author haoxr
 * @description 微信小程序认证接口
 * @createTime 2021/5/20 23:37
 */
@RequiredArgsConstructor
@Service
public class WxOauthService {

    private final BmsAppFeignClient bmsAppFeignClient;

    private final UpmsFeignClient upmsFeignClient;

    private final WxMaService wxMaService;

    private final RedisService redisService;

    private final JwtGenerator jwtGenerator;

    public WxLoginResult login(WxLoginParams params) {
        WxLoginResult result = new WxLoginResult();
        WxMaJscode2SessionResult session;
        try {
            // 获取登录后的session信息
            session = wxMaService.getUserService().getSessionInfo(params.getCode());
        } catch (WxErrorException e) {
            throw new BusinessException("登录失败");
        }
        Result<BlogMember> blogMemberResult = bmsAppFeignClient.getByOpenId(session.getOpenid());
        if (!ResultCode.SUCCESS.code().equals(blogMemberResult.getCode())) {
            throw new BusinessException("登录失败");
        }
        BlogMember blogMember = blogMemberResult.getData();
        if (blogMember == null) {
            // 用户不存在，注册成为新用户
            BlogMemberParams blogMemberParams = new BlogMemberParams();
            blogMemberParams.setOpenId(session.getOpenid());
            blogMemberParams.setNickName("新用户");
            blogMemberResult = bmsAppFeignClient.addMember(blogMemberParams);
            if (!ResultCode.SUCCESS.code().equals(blogMemberResult.getCode())) {
                throw new BusinessException("登录失败");
            }
            blogMember = blogMemberResult.getData();
        } else {
            if (ResultCode.USER_ACCOUNT_DISABLE.code().equals(blogMember.getEnabled())) {
                throw new InternalAuthenticationServiceException(ResultCode.USER_ACCOUNT_DISABLE.msg());
            }
        }
        MemberInfo memberInfo = DozerConvertor.convertor(blogMember, MemberInfo.class);
        result.setUserInfo(memberInfo);
        // 设置用户信息
        redisService.set(RedisKeyConstant.APP_USER + blogMember.getId(), memberInfo, 30L, TimeUnit.DAYS);
        // 自定义JWT生成
        // 1. JWT授权，一般存放用户的角色标识，用于资源服务器（网关）鉴权
        Set<String> authorities = new HashSet<>();
        // 2. JWT增强，携带用户ID等信息
        Map<String, Object> additional = new HashMap<>(4);
        additional.put(AuthConstant.DETAILS_USER_ID, blogMember.getId());
        additional.put(AuthConstant.DETAILS_USER_NAME, blogMember.getNickName());
        additional.put(AuthConstant.DETAILS_CLIENT_ID, GlobalConstant.APP_CLIENT);
        String accessToken = jwtGenerator.createAccessToken(authorities, additional);
        result.setToken(OauthToken.accessToken(accessToken));
        // 获取pages
        Result<List<DictItem>> dictItemResult = upmsFeignClient.getDictItem(DictCodeConstant.APP_PAGES);
        if (ResultCode.SUCCESS.code().equals(dictItemResult.getCode())) {
            result.setPages(dictItemResult.getData());
        }
        return result;
    }
}
